﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BlogController.cs" company="ecblog.codeplex.com">
//   emrecoskun.net - ibrahimatay.com
// </copyright>
// <summary>
//   Blog controller
// </summary>
// --------------------------------------------------------------------------------------------------------------------
 
namespace EcBlog.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Data.Entity;
    using System.IO;
    using System.Linq;
    using System.Net.Mail;
    using System.Text;
    using System.Web.Mvc;

    using EcBlog.Data;
    using EcBlog.Extensions;
    using EcBlog.Models;
    using EcBlog.Properties;

    using Recaptcha;

    /// <summary>
    /// Blog controller
    /// </summary>
    [Export, PartCreationPolicy(CreationPolicy.NonShared)]
    public class BlogController : Controller
    {
        #region ctor
        private readonly IEcUnitOfWork unitOfWork;

        /// <summary>
        /// Initializes a new instance of the <see cref="BlogController"/> class.
        /// </summary>
        /// <param name="unitOfWork">The unit of work.</param>
        [ImportingConstructor]
        public BlogController(IEcUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
            RecaptchaControlMvc.PrivateKey = unitOfWork.SiteSettingRepository[SiteSettingKey.RecaptchaPrivateKey];
            RecaptchaControlMvc.PublicKey = unitOfWork.SiteSettingRepository[SiteSettingKey.RecaptchaPublicKey];
        } 
        #endregion
     
        #region Dynamic Page
        /// <summary>
        /// Dynamics the page.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <returns>The dynamic page view.</returns>
        public ActionResult DynamicPage(Page page)
        {
            this.ViewBag.Description = this.unitOfWork.SiteSettingRepository[SiteSettingKey.SiteDescription];
            this.ViewBag.SiteTitle = string.Format("{0} | {1}", this.unitOfWork.SiteSettingRepository[SiteSettingKey.SiteTitle], page.Title);
            return this.View("DynamicPage", page);
        }

        /// <summary>
        /// Dynamics the page slug.
        /// </summary>
        /// <param name="pageSlug">The page slug.</param>
        /// <returns>The dynamic page view.</returns>
        public ActionResult DynamicPageSlug(string pageSlug)
        {
            try
            {
                pageSlug = this.GetRawSlug(pageSlug);
                var page =
                    this.unitOfWork.PageRepository
                        .Get(c => c.Tags, c => c.Comments, c => c.Layout)
                        .First(c => c.Slug.Contains(pageSlug) && c.Published < DateTime.Now && c.IsPublish);
                page.Comments = page.Comments.Where(l => l.IsPublished).ToList();

                return this.DynamicPage(page);
            }
            catch (Exception exception)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(exception);
                return this.BlogPosts();
            }
        }

        /// <summary>
        /// Dynamics the page by id.
        /// </summary>
        /// <param name="pageId">The page id.</param>
        /// <returns>the page</returns>
        public ActionResult DynamicPageById(long pageId)
        {
            try
            { 
                var page =
                    this.unitOfWork.PageRepository
                        .Get(c => c.Tags, c => c.Comments, c => c.Layout)
                        .First(c => c.PageId == pageId);
                page.Comments = page.Comments.Where(l => l.IsPublished).ToList();

                return this.DynamicPage(page);
            }
            catch (Exception exception)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(exception);
                return this.BlogPosts();
            }
        }

        /// <summary>
        /// Get the blog post view.
        /// </summary>
        /// <param name="year">The year.</param>
        /// <param name="month">The month.</param>
        /// <param name="day">The day.</param>
        /// <param name="slug">The slug.</param>
        /// <returns>The blog post view.</returns>
        [OutputCache(Duration = 30)]
        public ActionResult BlogPost(int year, int? month, int? day, string slug)
        {
            slug = this.GetRawSlug(slug); 

            var count = this.unitOfWork.PageRepository.Get()
                .Count(c => c.Published.Year == year 
                    && ((month != null && c.Published.Month == month) || month == null)
                    && ((day != null && c.Published.Day == day) || day == null)
                    && c.Slug.Contains(slug) 
                    && c.Published < DateTime.Now
                    && c.IsPublish);

            if (count != 1)
            {
                return this.RedirectToAction("Search", new { searchKey = slug });
            }

            var page =
                this.unitOfWork.PageRepository.Get(c => c.Tags, c => c.Comments, c => c.Layout)
                .First(c => c.Published.Year == year 
                    && ((month != null && c.Published.Month == month) || month == null)
                    && ((day != null && c.Published.Day == day) || day == null) 
                    && c.Slug.Contains(slug)
                    && c.Published < DateTime.Now 
                    && c.IsPublish);

            page.Comments = page.Comments.Where(l => l.IsPublished).ToList();

            return this.DynamicPage(page);
        } 
        #endregion

        #region Dynamic Pages
        /// <summary>
        /// Get the dynamic blog pages list view.
        /// </summary>
        /// <param name="pages">The pages.</param>
        /// <param name="count">The count.</param>
        /// <param name="pageNo">The page no.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <param name="paging">The paging.</param>
        /// <param name="filter">The filter.</param>
        /// <returns>The dynamic blog pages list view.</returns>
        public ActionResult DynamicPages(List<Page> pages, int count, int pageNo, int pageSize, string paging, string filter)
        { 
            this.ViewBag.Description = this.unitOfWork.SiteSettingRepository[SiteSettingKey.SiteDescription];
            this.ViewBag.SiteTitle = this.unitOfWork.SiteSettingRepository[SiteSettingKey.SiteTitle];
            var defaultLayoutName = this.unitOfWork.SiteSettingRepository[SiteSettingKey.DefaultLayoutName];
            var defaultLayout = this.unitOfWork.LayoutRepository.Get().First(c => c.Name == defaultLayoutName);
            var urlSuffix = this.unitOfWork.SiteSettingRepository[SiteSettingKey.UrlSuffix];

            var totalPage = (int)Math.Ceiling((decimal)count / pageSize);
            return this.View(
                "DynamicPages",
                new DynamicPagesViewModel
                {
                    Pages = pages,
                    Layout = defaultLayout,
                    ShowPrevious = pageNo > 1,
                    TotalPage = totalPage,
                    CurrentPage = pageNo,
                    ShowNext = (int)Math.Ceiling((decimal)count / pageSize) > pageNo,
                    Paging = paging + urlSuffix,
                    FilterDescription = string.IsNullOrEmpty(filter) ? null : filter + " - " + ModelLocalization.Page + pageNo + " / " + totalPage
                });
        }

        /// <summary>
        /// Get the tag post list view.
        /// </summary>
        /// <param name="tagSlug">The tag slug.</param>
        /// <param name="pageNo">The page no.</param>
        /// <returns>The tag post list view.</returns>
        public ActionResult TagPosts(string tagSlug, int pageNo = 1)
        {
            tagSlug = this.GetRawSlug(tagSlug);
            var size = int.Parse(this.unitOfWork.SiteSettingRepository[SiteSettingKey.NumberOfVisiblePageInWeb]);
            var tagName = this.unitOfWork.TagRepository.Get().Where(c => c.TagSlug == tagSlug).Select(c => c.TagName).First();
            this.ViewBag.SiteTitle = string.Format("{0} | {1}", this.unitOfWork.SiteSettingRepository[SiteSettingKey.SiteTitle], tagName);

            var count =
                this.unitOfWork.TagRepository.Get(c => c.Pages)
                    .Where(t => t.TagSlug == tagSlug)
                    .SelectMany(c => c.Pages).Count(c => c.ShowPostList && c.IsPublish && c.Published < DateTime.Now);
            var pages =
               this.unitOfWork.TagRepository.Get(c => c.Pages)
                    .Where(t => t.TagSlug == tagSlug)
                    .SelectMany(c => c.Pages)
                    .Where(c => c.ShowPostList && c.IsPublish && c.Published < DateTime.Now)
                    .AsQueryable().Include(c => c.Tags).Include(c => c.Comments)
                    .OrderByDescending(c => c.Published)
                    .Skip(size * (pageNo - 1)).Take(size).ToList();
            pages.ForEach(c => c.Comments = c.Comments.Where(l => l.IsPublished).ToList());

            return this.DynamicPages(
                pages,
                count,
                pageNo,
                size,
                "category" + "/" + tagSlug,
                string.Format(ModelLocalization.TagArchive, tagName));
        }

        /// <summary>
        /// Get the blog post list view.
        /// </summary>
        /// <param name="pageNo">The page no.</param>
        /// <returns>The blog post list view.</returns>
        public ActionResult BlogPosts(int pageNo = 1)
        {
            var size = int.Parse(this.unitOfWork.SiteSettingRepository[SiteSettingKey.NumberOfVisiblePageInWeb]);
            var count = this.unitOfWork.PageRepository.Get().Count(c => c.ShowPostList && c.IsPublish);
            var pages =
                this.unitOfWork.PageRepository.Get(c => c.Tags, c => c.Comments)
                    .OrderByDescending(c => c.Published)
                    .Where(c => c.ShowPostList && c.IsPublish && c.Published < DateTime.Now)
                    .Skip(size * (pageNo - 1)).Take(size).ToList();
            pages.ForEach(c => c.Comments = c.Comments.Where(l => l.IsPublished).ToList());

            return this.DynamicPages(pages, count, pageNo, size, "page", pageNo > 1 ? ModelLocalization.PostArchive : string.Empty);
        }

        /// <summary>
        /// Get the post archives.
        /// </summary>
        /// <param name="year">The year.</param>
        /// <param name="month">The month.</param>
        /// <param name="pageNo">The page no.</param>
        /// <returns>The post archives</returns>
        public ActionResult BlogPostArchives(int year, int month, int pageNo = 1)
        {
            var size = int.Parse(this.unitOfWork.SiteSettingRepository[SiteSettingKey.NumberOfVisiblePageInWeb]);
            var count = this.unitOfWork.PageRepository.Get()
                .Count(c => c.Published.Year == year && c.Published.Month == month && c.Published < DateTime.Now && c.ShowPostList && c.IsPublish);
            var pages =
                this.unitOfWork.PageRepository.Get(c => c.Tags, c => c.Comments)
                    .OrderByDescending(c => c.Published)
                    .Where(
                        c =>
                        c.ShowPostList && c.IsPublish && c.Published.Year == year && c.Published.Month == month &&
                        c.Published < DateTime.Now)
                    .Skip(size * (pageNo - 1)).Take(size).ToList();
            pages.ForEach(c => c.Comments = c.Comments.Where(l => l.IsPublished).ToList());

            return this.DynamicPages(
                pages,
                count,
                pageNo,
                size,
                "archive/" + year + "/" + month,
                string.Format(ModelLocalization.DateArchive, year + " / " + month));
        }

        /// <summary>
        /// Searches the specified search key.
        /// </summary>
        /// <param name="searchKey">The search key.</param>
        /// <param name="pageNo">The page no.</param>
        /// <returns>The search result view.</returns>
        public ActionResult Search(string searchKey, int pageNo = 1)
        {
            if (string.IsNullOrEmpty(searchKey))
            {
                return this.RedirectToAction("BlogPosts");
            }

            var size = int.Parse(this.unitOfWork.SiteSettingRepository[SiteSettingKey.NumberOfVisiblePageInWeb]);
            var count = this.unitOfWork.PageRepository.Get()
                .Where(c => c.ShowPostList && c.IsPublish && c.ContentHtml.Contains(searchKey) && c.Published < DateTime.Now)
                .OrderByDescending(c => c.Published)
                .Count();
            var pages =
                this.unitOfWork.PageRepository.Get(c => c.Tags, c => c.Comments)
                    .OrderByDescending(c => c.Published)
                    .Where(c => c.ShowPostList && c.IsPublish && c.ContentHtml.Contains(searchKey) && c.Published < DateTime.Now)
                    .Skip(size * (pageNo - 1)).Take(size).ToList();
            pages.ForEach(c => c.Comments = c.Comments.Where(l => l.IsPublished).ToList());

            return this.DynamicPages(
                pages,
                count,
                pageNo,
                size,
                "search/" + searchKey,
                string.Format(ModelLocalization.SearchResults, searchKey));
        } 
        #endregion
        
        #region Error
        /// <summary>
        /// Get error view with the specified URL.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <returns>The error view.</returns>
        public ActionResult Error(string url)
        {
            return this.View();
        } 
        #endregion

        #region Comment
        /// <summary>
        /// Get the new comment view.
        /// </summary>
        /// <param name="pageId">The page id.</param>
        /// <returns>The new comment view</returns>
        [ChildActionOnly]
        public ActionResult NewComment(long pageId)
        {
            return this.PartialView(new NewCommentViewModel { PageId = pageId, Comment = new Comment() });
        }

        /// <summary>
        /// Get the new comment view.
        /// </summary>
        /// <param name="viewModel">The view model.</param>
        /// <param name="captchaValid">The captcha valid.</param>
        /// <returns>The new comment view.</returns>
        [RecaptchaControlMvc.CaptchaValidator]
        [HttpPost]
        public ActionResult NewComment(NewCommentViewModel viewModel, bool? captchaValid)
        {
            if (captchaValid.HasValue && !captchaValid.Value)
            {
                return
                    this.PartialView(
                        new NewCommentViewModel
                            {
                                PageId = viewModel.PageId,
                                Comment = new Comment(),
                                Message = ModelLocalization.NewComment_Invalid
                            });
            }

            var page =
                this.unitOfWork.PageRepository.Get().Where(c => c.PageId == viewModel.PageId)
                    .Select(c => new { c.Slug, c.Title }).First();
            viewModel.Comment.PageId = viewModel.PageId;
            this.unitOfWork.CommentRepository.Create(viewModel.Comment);

            var needSendAlertMail = bool.Parse(this.unitOfWork.SiteSettingRepository[SiteSettingKey.SendAlertMailOnNewComment]);
            if (needSendAlertMail)
            {
                this.SendAlertMail(
                    this.unitOfWork.SiteSettingRepository[SiteSettingKey.CommentAlertEMail],
                    viewModel.Comment.CommentHtml + Environment.NewLine,
                    page.Slug,
                    page.Title);
            }

            return
                this.PartialView(
                    new NewCommentViewModel { PageId = viewModel.PageId, Comment = new Comment(), Message = ModelLocalization.NewComment_Sended });
        } 
        #endregion 

        #region Contact
        /// <summary>
        /// Get the contract view.
        /// </summary>
        /// <returns>The contract view.</returns> 
        public ActionResult Contact()
        {
            this.ViewBag.Status = string.Empty;
            this.ViewBag.ContactMessage = this.unitOfWork.SiteSettingRepository[SiteSettingKey.ContactMessage];
            return this.View(new ContactViewModel());
        }

        /// <summary>
        /// Save contact that the specified view model.
        /// </summary>
        /// <param name="viewModel">The view model.</param>
        /// <param name="captchaValid">The captcha valid.</param>
        /// <returns>
        /// The contact view.
        /// </returns>
        [HttpPost]
        [RecaptchaControlMvc.CaptchaValidator]
        public ActionResult Contact(ContactViewModel viewModel, bool? captchaValid)
        {
            if (this.ModelState.IsValid && captchaValid.HasValue && captchaValid.Value)
            {
                var mail =
                    new MailViewModel
                    {
                        From = this.unitOfWork.SiteSettingRepository[SiteSettingKey.Email],
                        Message = viewModel.Name + " ( " + viewModel.Email + " ) : " + Environment.NewLine + viewModel.Message,
                        PageTitle = ModelLocalization.ContactViewTitle,
                        PageUrl = this.unitOfWork.SiteSettingRepository[SiteSettingKey.SiteUrl],
                        Subject = viewModel.Title,
                        To = this.unitOfWork.SiteSettingRepository[SiteSettingKey.CommentAlertEMail]
                    };
                this.SendMailWithMailView(mail);
                this.ViewBag.Status = ModelLocalization.BlogController_Contact_Message_has_been_send;
            }

            this.ViewBag.ContactMessage = this.unitOfWork.SiteSettingRepository[SiteSettingKey.ContactMessage];
            return this.View(viewModel);
        } 
        #endregion

        #region Support Methods

        /// <summary>
        /// Sends the mail with mail view.
        /// </summary>
        /// <param name="viewModel">The view model.</param>
        public void SendMailWithMailView(MailViewModel viewModel)
        {
            ViewEngineResult view = null;
            foreach (var engine in ViewEngines.Engines)
            {
                view = engine.FindPartialView(this.ControllerContext, "Mail", false);
                if (view.View != null)
                {
                    break;
                }
            }

            if (view == null || view.View == null)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Mail view not found."));
                return;
            }

            var mailMessage = new MailMessage(viewModel.From, viewModel.To)
            {
                Subject = viewModel.Subject,
                Body = this.RenderViewAsString(view.View, viewModel),
                BodyEncoding = Encoding.UTF8,
                IsBodyHtml = true,
                From = new MailAddress(viewModel.From),
            };

            // mailMessage.To.Add(new MailAddress(_settingRepository[SiteSettingKey.Email]));
            var smtpClient = new SmtpClient();
            try
            {
                smtpClient.Host = this.unitOfWork.SiteSettingRepository[SiteSettingKey.EmailHost];
                smtpClient.Port = int.Parse(this.unitOfWork.SiteSettingRepository[SiteSettingKey.EmailPort]);

                smtpClient.EnableSsl = bool.Parse(this.unitOfWork.SiteSettingRepository[SiteSettingKey.EmailEnableSsl]);

                smtpClient.Credentials =
                   new System.Net.NetworkCredential(
                       this.unitOfWork.SiteSettingRepository[SiteSettingKey.EmailUserName],
                       this.unitOfWork.SiteSettingRepository[SiteSettingKey.EmailUserPassword]);
                smtpClient.Send(mailMessage);
            }
            catch (Exception exception)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(exception);
            }
        }

        private void SendAlertMail(string to, string comment, string slug, string title)
        {
            var page = this.unitOfWork.PageRepository.Get().First(x => x.Slug == slug);
            var mail = new MailViewModel
            {
                From = this.unitOfWork.SiteSettingRepository[SiteSettingKey.Email],
                Message = comment,
                PageTitle = title,
                PageUrl = UrlHelpersExtensions.GetSlugUrl(null, page.Published, slug, string.Empty),
                Subject = this.unitOfWork.SiteSettingRepository[SiteSettingKey.CommentAlertMailSubject],
                To = to
            };
            this.SendMailWithMailView(mail);
        } 

        private string RenderViewAsString(IView view, MailViewModel model)
        {
            using (var writer = new StringWriter())
            {
                var modeltemp = this.ViewData.Model;
                this.ViewData.Model = model;
                var viewContext = new ViewContext(this.ControllerContext, view, this.ViewData, this.TempData, writer);
                view.Render(viewContext, writer);
                this.ViewData.Model = modeltemp;
                return writer.GetStringBuilder().ToString();
            }
        } 

        private string GetRawSlug(string slug)
        {
            bool httpAddresInUse = bool.Parse(this.unitOfWork.SiteSettingRepository[SiteSettingKey.UseHttpInSlugAddress]);

            return httpAddresInUse && slug.StartsWith("http") ? slug.Replace("http://", string.Empty).Replace(@"https://", string.Empty) : slug;
        }  
        #endregion
    }
}